package com.ceair.flight.refund.service.ota.impl;

import com.ceair.flight.refund.dto.external.RefundPriceResult;
import com.ceair.flight.refund.dto.external.RefundPriceResultList;
import com.ceair.flight.refund.dto.external.RefundableRequest;
import com.ceair.flight.refund.dto.external.RefundableResult;
import com.ceair.flight.refund.dto.ota.CalcFeeRes;
import com.ceair.flight.refund.dto.ota.CalcFeeV2Req;
import com.ceair.flight.refund.dto.ota.RefundFeePaxDto;
import com.ceair.flight.refund.dto.ota.RefundFeeSegDto;
import com.ceair.flight.refund.dto.ota.RefundInfo;
import com.ceair.flight.refund.location.model.FlightOrderDO;
import com.ceair.flight.refund.location.model.OrderDO;
import com.ceair.flight.refund.location.model.PaxDO;
import com.ceair.flight.refund.location.model.SegmentDO;
import com.ceair.flight.refund.location.model.TicketDO;
import com.ceair.flight.refund.location.service.IRefundLocationService;
import com.ceair.flight.refund.service.IOrderService;
import com.ceair.flight.refund.service.ota.IOtaRefundCalculatorService;
import com.ceair.flight.refund.util.Constants;
import com.ceair.flight.refund.util.DateUtil;
import com.ceair.flight.refund.util.GsonUtil;
import com.ceair.flight.refund.util.StringUtil;
import com.ceair.muservice.basicdata.dto.airport.AirportDto;
import com.ceair.muservice.basicdata.dto.channel.ChannelManageDto;
import com.ceair.muservice.basicdata.feign.util.AirportUtil;
import com.ceair.muservice.basicdata.feign.util.ChannelUtil;
import com.ceair.muservice.common.exception.MuServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Service("otaRefundCalculatorService")
@Path("/ota")
public class OtaRefundCalculatorServiceImpl implements IOtaRefundCalculatorService {

    private static final Logger LOG = LoggerFactory.getLogger(OtaRefundCalculatorServiceImpl.class);

    @Autowired
    private IRefundLocationService refundLocationService;

    @Autowired
    private IOrderService orderService;

    @Override
    @POST
    @Path("/calcFeeV2")
    @Consumes({"application/json"})
    @Produces(MediaType.APPLICATION_JSON)
    public CalcFeeRes calcFeeV2(CalcFeeV2Req request) {
        CalcFeeRes res = new CalcFeeRes();
//        calcFeeRes.setMessage("根据您阅读过的东航退票客规，收取您￥"+refundFee+"元的退票手续费");
        res.setResultCode(Constants.SUCCESS_CODE);
        res.setResultMsg(Constants.SUCCESS_MSG);
        if (!checkRequest(request)){
            res.setResultCode(Constants.ERROR_CODE);
            res.setResultMsg(Constants.PARAMS_ERROR_MSG);
            return res;
        }

        OrderDO orderDO = orderService.queryFlightOrderDetail(request.getOrderNo());
        if (orderDO == null){
            res.setResultCode(Constants.ERROR_CODE);
            res.setResultMsg(Constants.SERVER_ERROR_MSG);
            return res;
        }
        List<Long> psgIds = new ArrayList<>();
        List<RefundInfo> refundInfoList = request.getRefundInfos();
        if (CollectionUtils.isEmpty(refundInfoList)){
            res.setResultCode(Constants.ERROR_CODE);
            res.setResultMsg(Constants.PARAMS_ERROR_MSG);
            return res;
        }
        for (RefundInfo refundInfo:refundInfoList){
            Long id = refundInfo.getId();
            String procTp = refundInfo.getProdType();
            if (Constants.PRO_TYPE_AIR.equals(procTp)){
                psgIds.add(id);
            }
        }

        try {
            List<PaxDO> refundPaxList = new ArrayList<>();
            Set<FlightOrderDO> flightOrderDOS = orderDO.getFlightOrders();
            for (FlightOrderDO flightOrderDO:flightOrderDOS){
                Set<PaxDO> paxDOS = flightOrderDO.getPaxs();
                for (PaxDO paxDO:paxDOS){
                    Integer status = paxDO.getModifySt();
                    if (status.equals(2)){
                        Long paxId = paxDO.getPassengerId();
                        if (psgIds.contains(paxId)){
                            refundPaxList.add(paxDO);
                        }
                    }
                }
            }
            Map<String, Set<String>> tktMap = new HashMap<>();
            for (PaxDO paxDO:refundPaxList){
                Set<TicketDO> tktSet = paxDO.getTickets();
                for (TicketDO ticket:tktSet){
                    String flightOrderNo = ticket.getFlightOrderNo();
                    Set<String> tktNoSet = tktMap.get(flightOrderNo);
                    if (tktNoSet == null){
                        tktNoSet = new HashSet<>();
                        tktMap.put(flightOrderNo,tktNoSet);
                    }
                    tktNoSet.add(ticket.getTktNo());
                }
            }
            List<RefundPriceResultList>  resultList = calFee(request,tktMap);
            res = buildResult(resultList,request,refundPaxList);
        }catch (Exception e){
            String str = GsonUtil.toJson(request);
            LOG.error("Calculator Refund Fee error,param: "+str,e);
            res.setResultCode(Constants.ERROR_CODE);
            res.setResultMsg(Constants.SERVER_ERROR_MSG);
            return res;
        }catch (Error ex){
            String str = GsonUtil.toJson(request);
            LOG.error("Calculator Refund Fee error,param: "+str,ex);
            res.setResultCode(Constants.ERROR_CODE);
            res.setResultMsg(Constants.SERVER_ERROR_MSG);
            return res;
        }

        return res;
    }

    private CalcFeeRes buildResult(List<RefundPriceResultList> resultList, CalcFeeV2Req request,  List<PaxDO> refundPaxList) {

        Map<String, AirportDto> airportMap = getAirportInfo(refundPaxList);
        ChannelManageDto channelManageDto = ChannelUtil.findChannelByChannelCode(request.getSalesChannel());
        String language = channelManageDto.getLanguageCode();
        CalcFeeRes res = new CalcFeeRes();
        BigDecimal refundAmt = BigDecimal.ZERO;
        BigDecimal refundFee = BigDecimal.ZERO;
        res.setResultCode(Constants.SUCCESS_CODE);
        res.setResultMsg(Constants.SUCCESS_MSG);
        List<RefundFeePaxDto> paxDtoList = new ArrayList<>();
        res.setRefundFeePaxDtoList(paxDtoList);
        Long applyType = request.getApplyType();
        for (PaxDO paxDO:refundPaxList){//遍历退票人信息
            RefundFeePaxDto refundFeePaxDto = new RefundFeePaxDto();
            paxDtoList.add(refundFeePaxDto);
            refundFeePaxDto.setIdNo(paxDO.getPassNo());
            refundFeePaxDto.setIdType(paxDO.getPassTp());
            refundFeePaxDto.setPaxName(paxDO.getPaxFullName());
            List<RefundFeeSegDto> refundFeeSegDtoList =new ArrayList<>();
            refundFeePaxDto.setRefundFeeSegDtoList(refundFeeSegDtoList);
            Set<TicketDO> tktSet = paxDO.getTickets();
            Set<SegmentDO> segmentDOS = paxDO.getSegments();
            for (TicketDO ticketDO:tktSet){//遍历票信息
                String tktNo = ticketDO.getTktNo();
                Long tktId = ticketDO.getTktId();
                for (RefundPriceResultList refundPriceResultList:resultList){
                    RefundableResult refundableResult = null;
                    refundableResult = refundPriceResultList.getSingleResult(tktNo);//根据票号获取是否可退信息
                    if (refundableResult == null || !refundableResult.isRefundableInPolicy()){
                        continue;
                    }
                    RefundPriceResult refundPriceResult = null;
                    if(Constants.OTA_VOLRFD_CD.equals(applyType)){//获取退票费用相关信息
                        //自愿退票
                        refundPriceResult = refundPriceResultList.getVolPrice(tktNo);
                    }else {
                        refundPriceResult = refundPriceResultList.getInvolPrice(tktNo);
                    }
                    String paxTktNo = refundFeePaxDto.getTicketNo();
                    if(StringUtil.isBlank(paxTktNo)){
                        refundFeePaxDto.setTicketNo(tktNo);
                    }else {
                        refundFeePaxDto.setTicketNo(paxTktNo + "/"+tktNo);
                    }
                    //退给旅客的可退金额
                    refundAmt = refundAmt.add(refundPriceResult.getRefundAmount());
                    refundFee = refundFee.add(refundPriceResult.getFeeAmount());//退票手续费
                    int refundableIdx = refundableResult.getRefundableIndex();
                    for (SegmentDO segmentDO:segmentDOS){
                        Integer segSq = segmentDO.getSegSq();
                        Long tktIdSeg = segmentDO.getTktID();
                        if (tktIdSeg .equals(tktId) && segSq >= refundableIdx){
                            RefundFeeSegDto segDto = new RefundFeeSegDto();
                            segDto.setArrivalCode(segmentDO.getArrAirpCd());
                            //TODO
                            AirportDto arrInfo = airportMap.get(segmentDO.getArrAirpCd());
                            String arrAptName= arrInfo.getName(language);
                            segDto.setArrivalName(arrAptName);//到达机场名
                            segDto.setCabinCode(segmentDO.getClassTp());
                            segDto.setDepartureCode(segmentDO.getDepAirpCd());
                            AirportDto depInfo = airportMap.get(segmentDO.getDepAirpCd());
                            String depAtpName = depInfo.getName(language);
                            segDto.setDepartureName(depAtpName);//出发机场名
                            String dateStr = DateUtil.toString(segmentDO.getDepDt(),DateUtil.yyyy_MM_dd);
                            String depTime = dateStr + " " + segmentDO.getDepTm();
                            segDto.setDepTime(depTime);
                            String flightNo = segmentDO.getCarrCd() + segmentDO.getFlightNo();
                            segDto.setFlightNo(flightNo);
                            refundFeeSegDtoList.add(segDto);
                        }
                    }
                }
            }

        }

        res.setRefundAmount(Double.parseDouble(refundAmt.toString()));
        res.setRefundFee(Double.parseDouble(refundFee.toString()));

        return res;
    }

    private Map<String, AirportDto> getAirportInfo(List<PaxDO> refundPaxList) {
        Set<String> airportSet = new HashSet<>();
        for (PaxDO paxDO:refundPaxList){
            Set<SegmentDO> segmentDOS = paxDO.getSegments();
            for (SegmentDO segmentDO:segmentDOS){
                airportSet.add(segmentDO.getDepAirpCd());
                airportSet.add(segmentDO.getArrAirpCd());
            }
        }
        List<String> codeList = new ArrayList<>(airportSet);
        return refundLocationService.findNationCityAirNameByCodes(codeList);
    }


    private List<RefundPriceResultList> calFee(CalcFeeV2Req request, Map<String, Set<String>> tktMap) throws MuServiceException {
        List<RefundPriceResultList> result = new ArrayList<>();
        if (tktMap == null || tktMap.isEmpty()){
            return result;
        }
        Long applyType = request.getApplyType();

        if(Constants.OTA_VOLRFD_CD.equals(applyType)){
            //自愿退票
            for (Map.Entry<String, Set<String>> entry:tktMap.entrySet()){
                String key = entry.getKey();
                Set<String> value = entry.getValue();
                RefundableRequest req = new RefundableRequest();
                req.setFlightOrderNo(key);
                req.setTickets(value);
                req.setSalesChannel(request.getSalesChannel());
                RefundPriceResultList refundPriceResultList = refundLocationService.calculateVolPrice(req);
                result.add(refundPriceResultList);
            }
        }else {
            //非自愿
            for (Map.Entry<String, Set<String>> entry:tktMap.entrySet()){
                String key = entry.getKey();
                Set<String> value = entry.getValue();
                RefundableRequest req = new RefundableRequest();
                req.setFlightOrderNo(key);
                req.setTickets(value);
                req.setSalesChannel(request.getSalesChannel());
                RefundPriceResultList refundPriceResultList = refundLocationService.calculateInVolPrice(req);
                result.add(refundPriceResultList);
            }
        }

        return result;
    }

    private boolean checkRequest(CalcFeeV2Req req) {
        boolean result = true;
        String channelCode = req.getSalesChannel();
        String orderNo = req.getOrderNo();
        Long applyType = req.getApplyType();
        if (StringUtil.isBlank(channelCode) || StringUtil.isBlank(orderNo) || CollectionUtils.isEmpty(req.getRefundInfos())){
            return false;
        }
        if (applyType == null || applyType <= 0){
            return false;
        }
        if(Constants.OTA_ANOTHER_BUY_CD.equals(req.getApplyType())){
            if (StringUtil.isBlank(req.getRepurchaseTicket())){
                return false;
            }
        }

        return result;
    }
}